#!/usr/bin/python

# --------------------------------------------------------------------- #
# mcastcmd.py	- Multicast Command Module.				#
#									#
#		  Provide functions used to send command via multicast	#
#		  channel.						#
#									#
# version 0.1	- cj (2010-08-20) init.					#
# --------------------------------------------------------------------- #

import os
import sys
import time
import socket
import struct
import string
import subprocess

# --------------------------------------------------------------------- #
# Constants								#
# --------------------------------------------------------------------- #

MCASTCMD_HEADER		= "MCASTCMD"
MCASTCMD_VERSION	= "01"

AESPIPE			= "/usr/bin/aespipe"
PASSPHRASE		= "./passphrase.txt"
BASE64			= "/usr/bin/base64"
MD5SUM			= "/usr/bin/md5sum"

AESCRYPTED		= [ AESPIPE, "-P", PASSPHRASE ]
AESDECRYPTED		= [ AESPIPE, "-P", PASSPHRASE , "-d" ]
B64ENCODED		= [ BASE64 ]
B64DECODED		= [ BASE64, "--decode" ]
HASHMD5			= [ MD5SUM, "-" ]

DEFAULT_MCAST_PORT	= 55555
DEFAULT_MCAST_GRP	= "224.1.1.1"

# ---------------------------------------------------------------------
# pipe through commands
# ---------------------------------------------------------------------
def	systemPipe(cmd, buf):
	p = subprocess.Popen(	cmd,
				bufsize=0,
				stdin=subprocess.PIPE,
				stdout=subprocess.PIPE)

	p.stdin.write(buf)
	return p.communicate()[0]

# ---------------------------------------------------------------------
# Encrypt message
# ---------------------------------------------------------------------
def	aesCrypt(buf):
	return systemPipe(AESCRYPTED, buf)

# ---------------------------------------------------------------------
# Decrypt message
# ---------------------------------------------------------------------
def	aesDecrypt(buf):
	return systemPipe(AESDECRYPTED, buf)

# ---------------------------------------------------------------------
# Encode message
# ---------------------------------------------------------------------
def	b64encode(buf):
	return systemPipe(B64ENCODED, buf)

# ---------------------------------------------------------------------
# Decode message
# ---------------------------------------------------------------------
def	b64decode(buf):
	return systemPipe(B64DECODED, buf)

# ---------------------------------------------------------------------
# md5hash message
# ---------------------------------------------------------------------
def	md5hash(buf):
	return systemPipe(MD5HASH, buf).split(" ")[0]


# ---------------------------------------------------------------------
# hexdump line of buffer
# ---------------------------------------------------------------------
BYTEPERLINE	= 16

def	hexDumpLine(addr, buf, len):
	b = list(buf)

	line = "%04X    " % addr
	if len > BYTEPERLINE:
		len = BYTEPERLINE
	for n in range(0, len):
		line += "%02X " % ord(b[n])
	for n in range(len, BYTEPERLINE):
		line += "XX "

	line += "    "

	for n in range(0, len):
		c = ord(b[n])
		if (c < 0x32 or c > 0x80):
			c = ord('.')
		line += "%c" % c
	for n in range(len, BYTEPERLINE):
		line += " "
	
	line += "\n"

	return line

# ---------------------------------------------------------------------
# hexdump of Buffer
# ---------------------------------------------------------------------
def	hexDump(buf, length):
	start = 0
	dump = ""
	while start < length:
		stop = start + BYTEPERLINE
		if stop > length:
			stop = length
		dump += hexDumpLine(start, buf[start:stop], stop-start)
		start = stop
	return dump

# ---------------------------------------------------------------------
# Create Multicast Receiving Socket
# ---------------------------------------------------------------------
def	mcastCreateRecvSocket(addr, port):
	# first create socket, IPv4, UDP
	sock = socket.socket(	socket.AF_INET,
				socket.SOCK_DGRAM,
				socket.IPPROTO_UDP )

	# do we really need to reuse address?
	sock.setsockopt(	socket.SOL_SOCKET,
				socket.SO_REUSEADDR,
				1 )

	# then binding
	sock.bind(('', port))

	m = struct.pack(	"4sl",
				socket.inet_aton(addr),
				socket.INADDR_ANY )

	# next make it multicast
	sock.setsockopt(	socket.IPPROTO_IP,
				socket.IP_ADD_MEMBERSHIP,
				m )
	return sock

# ---------------------------------------------------------------------
# Create UDP socket for sending command
# ---------------------------------------------------------------------
def	mcastCreateSendSocket(addr, port):
	sock = socket.socket(	socket.AF_INET,
				socket.SOCK_DGRAM,
				socket.IPPROTO_UDP )

	sock.connect((addr, port))
	return sock

# Header
#	1 - "MCASTCMD"	- Tag
#	2 - "01"	- Version String
#       3 - TimeStamp	- Second From Epoch
#	4 - MD5SUM	- Command Hash Using MD5
#	5 - count	- Byte Count

# ---------------------------------------------------------------------
# Sending string to multicast
# ---------------------------------------------------------------------
def	mcastSend(s):
	h = md5hash(s)
	l = length(s)

#	print "hash of [%s] = '%s'" % (s, h)
#	hexDump(s, len(s))

	t = str(int(time.time()))
	r =	MCASTCMD_HEADER + ':' + \
		MCASTCMD_VERSION + ':' + \
		t + ':' + h + ':' + l + '\n' + s
	return b64encode(aesCrypt(r))

# ---------------------------------------------------------------------
# Receive string from multicast
# ---------------------------------------------------------------------
def	mcastRecv(s):
	print "mcastrecv1: s = ", s
	news = aesDecrypt(b64decode(s))
	s = news

	print "mcastrecv2: s = ", s

	# get the first line, interpret as header
	# we didn't handle case of errro yet!
	l = s.split("\n")[0]
	n = string.join(s.split("\n")[1:],"\n")
	ls = l.split(":")

	h = ls[0]
	v = ls[1]
	t = ls[2]
	m0 = ls[3]
	ll = ls[4]

	if h <> MCASTCMD_HEADER:
		print "header not match, expect "+MCASTCMD_HEADER+", got "+h
		return -1
	if v <> MCASTCMD_VERSION:
		print "version not match, expect "+MCASTCMD_VERSION+", got "+v
		return -1

	# message hash
	m = md5hash(n)

	print "hash of [%s] = '%s'" % (n, m)
	hexDump(n, len(n))

	if m0 <> m:
		print "HASH is not equal:"+m0+":"+m
		return -1

	now = int(time.time())
	then = int(t)
	return r

# ---------------------------------------------------------------------
# Main program
# ---------------------------------------------------------------------

if __name__ == "__main__":
	"""Multicast Command
	"""
	s = "Multicast Command \xaa \xbb \xcc \x12345"
	print hexDump(s, len(s))
	exit(0)

# --------------------------------------------------------------------- #
# end of file.								#
# --------------------------------------------------------------------- #
